//------------------------------------------------------------------------------
//  此代码版权（除特别声明或在XREF结尾的命名空间的代码）归作者本人若汝棋茗所有
//  源代码使用协议遵循本仓库的开源协议及附加协议，若本仓库没有设置，则按MIT开源协议授权
//  CSDN博客：https://blog.csdn.net/qq_40374647
//  哔哩哔哩视频：https://space.bilibili.com/94253567
//  Gitee源代码仓库：https://gitee.com/RRQM_Home
//  Github源代码仓库：https://github.com/RRQM
//  API首页：https://touchsocket.net/
//  交流QQ群：234762506
//  感谢您的下载和使用
//------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using TouchSocket.Core;

namespace TouchSocket.Dmtp.FileTransfer;

/// <summary>
/// 文件资源控制器。
/// </summary>
public class FileResourceController : DisposableObject, IFileResourceController
{
    private readonly Timer m_timer;
    /// <summary>
    /// 获取默认的文件资源控制器实例。
    /// </summary>
    /// <value>一个预定义的<see cref="FileResourceController"/>实例，用于统一处理文件资源。</value>
    public static FileResourceController Default { get; } = new FileResourceController();
    /// <summary>
    /// 文件资源控制器
    /// </summary>
    public FileResourceController()
    {
        this.Timeout = TimeSpan.FromSeconds(60);
        this.m_timer = new Timer((o) =>
        {
            var ints = new List<int>();
            foreach (var item in this.FileResourceStore)
            {
                if (DateTimeOffset.UtcNow - item.Value.LastActiveTime > this.Timeout)
                {
                    ints.Add(item.Key);
                }
            }

            foreach (var item in ints)
            {
                if (this.FileResourceStore.TryRemove(item, out var locator))
                {
                    locator.SafeDispose();
                }
            }
        }, null, 1000, 1000);
    }

    /// <inheritdoc/>
    public ConcurrentDictionary<int, FileResourceLocator> FileResourceStore { get; } = new ConcurrentDictionary<int, FileResourceLocator>();

    /// <summary>
    /// 超时时间。默认60秒。
    /// </summary>
    public TimeSpan Timeout { get; set; }

    /// <inheritdoc/>
    public virtual string GetFullPath(string root, string path)
    {
        this.ThrowIfDisposed();
        return path.IsNullOrEmpty() ? string.Empty : Path.IsPathRooted(path) ? path : Path.GetFullPath(Path.Combine(root, path));
    }

    /// <inheritdoc/>
    public virtual FileResourceLocator LoadFileResourceLocatorForRead(string path, int fileSectionSize)
    {
        this.ThrowIfDisposed();
        var fileResourceInfo = new FileResourceInfo(new FileInfo(path), fileSectionSize);
        var fileResourceLocator = new FileResourceLocator(fileResourceInfo);
        this.FileResourceStore.TryAdd(fileResourceInfo.ResourceHandle, fileResourceLocator);
        return fileResourceLocator;
    }

    /// <inheritdoc/>
    public virtual FileResourceLocator LoadFileResourceLocatorForWrite(string savePath, FileResourceInfo fileResourceInfo)
    {
        this.ThrowIfDisposed();
        if (this.FileResourceStore.TryGetValue(fileResourceInfo.ResourceHandle, out var fileResourceLocator))
        {
            return fileResourceLocator;
        }
        fileResourceInfo.ResetResourceHandle(fileResourceInfo.GetHashCode());
        fileResourceLocator = new FileResourceLocator(fileResourceInfo, savePath);
        this.FileResourceStore.TryAdd(fileResourceInfo.ResourceHandle, fileResourceLocator);
        return fileResourceLocator;
    }

    /// <inheritdoc/>
    public virtual int ReadAllBytes(FileInfo fileInfo, byte[] buffer)
    {
        this.ThrowIfDisposed();
        using (var byteBlock = FilePool.GetReader(fileInfo))
        {
            return byteBlock.Read(buffer);
        }
    }

    /// <inheritdoc/>
    public virtual bool TryGetFileResourceLocator(int resourceHandle, out FileResourceLocator fileResourceLocator)
    {
        this.ThrowIfDisposed();
        return this.FileResourceStore.TryGetValue(resourceHandle, out fileResourceLocator);
    }

    /// <inheritdoc/>
    public virtual bool TryReleaseFileResourceLocator(int resourceHandle, out FileResourceLocator locator)
    {
        this.ThrowIfDisposed();
        if (this.FileResourceStore.TryRemove(resourceHandle, out locator))
        {
            locator.SafeDispose();
            return true;
        }

        return false;
    }

    /// <inheritdoc/>
    public virtual void WriteAllBytes(string path, byte[] buffer, int offset, int length)
    {
        this.ThrowIfDisposed();
        using (var byteBlock = FilePool.GetWriter(path))
        {
            byteBlock.Write(new ReadOnlySpan<byte>(buffer, offset, length));
        }
    }

    /// <inheritdoc/>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            this.m_timer.SafeDispose();
            foreach (var item in this.FileResourceStore.Keys.ToArray())
            {
                if (this.FileResourceStore.TryRemove(item, out var locator))
                {
                    locator.SafeDispose();
                }
            }
        }

        base.Dispose(disposing);
    }
}